﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.ComponentModel;
using System.Windows.Media.Imaging;
using System.Collections.ObjectModel;
using ThreeByte.ActiveDeck.Data.UserResponse;

namespace ThreeByte.ActiveDeck.Config
{
    public class CategoriesConfig : PageConfig
    {

        #region Public Properties

        //TODO: Consolidate these properties
        public string Question {
            get {
                return Prompt;
            }
            set {
                Prompt = value;
            }
        }

        private string _prompt;
        public string Prompt {
            get {
                return _prompt;
            }
            set {
                _prompt = value;
                NotifyPropertyChanged("Prompt");
                NotifyPropertyChanged("Question");
            }
        }

        private ObservableCollection<Category> _categories = new ObservableCollection<Category>();
        public ObservableCollection<Category> Categories {
            get {
                return _categories;
            }
            set {
                _categories = value;
                NotifyPropertyChanged("Categories");
            }
        }

        private ObservableCollection<Word> _words = new ObservableCollection<Word>();
        public ObservableCollection<Word> Words {
            get {
                return _words;
            }
            set {
                _words = value;
                NotifyPropertyChanged("Words");
                NotifyPropertyChanged("HasAnswer");
            }
        }

        private string _correctAnswerOverride;
        public string CorrectAnswerOverride {
            get {
                return _correctAnswerOverride;
            }
            set {
                _correctAnswerOverride = value;
                NotifyPropertyChanged("CorrectAnswerOverride");
            }
        }

        private string _incorrectAnswerOverride;
        public string IncorrectAnswerOverride {
            get {
                return _incorrectAnswerOverride;
            }
            set {
                _incorrectAnswerOverride = value;
                NotifyPropertyChanged("IncorrectAnswerOverride");
            }
        }

        private bool _instantFeedback;
        public bool InstantFeedback {
            get {
                return _instantFeedback;
            }
            set {
                _instantFeedback = value;
                NotifyPropertyChanged("InstantFeedback");
            }
        }

        public bool HasAnswer {
            get {
                //check for correct category answers
                int correctAnswerCount = 0;
                foreach(Word w in Words) {
                    if(w.CorrectCategory != null) {
                        correctAnswerCount++;
                    }
                }

                return correctAnswerCount > 0;
            }
        }

        private string _theme;
        public string Theme {
            get {
                return _theme;
            }
            set {
                _theme = value;
                NotifyPropertyChanged("Theme");
            }
        }

        #endregion Public Properties

        public CategoriesConfig() {
            LayoutOrientation = Content.Orientation.Portrait; //By Default
        }

        public CategoriesConfig(XElement node, Dictionary<Guid, GraphicResource> resources)
            : base(node, resources) {
            Prompt = node.Element(ns + "Prompt").Attribute("Text").Value;

            Dictionary<int, Category> idCategoryMapping = new Dictionary<int, Category>();
            foreach(XElement c in node.Element(ns + "Categories").Elements(ns + "Category")) {
                Category newCategory = new Category(Uri.UnescapeDataString(c.Attribute("Text").Value));
                if(c.Attribute("ID") != null) {
                    idCategoryMapping[int.Parse(c.Attribute("ID").Value)] = newCategory;
                }
                Categories.Add(newCategory);
            }

            foreach(XElement w in node.Element(ns + "Words").Elements(ns + "Word")) {
                Word newWord = new Word(Uri.UnescapeDataString(w.Attribute("Text").Value));
                if(w.Attribute("CorrectCategory") != null) {
                    int catId = int.Parse(w.Attribute("CorrectCategory").Value);
                    if(idCategoryMapping.ContainsKey(catId)) {
                        newWord.CorrectCategory = idCategoryMapping[catId];
                    }
                }
                Words.Add(newWord);
            }

            XElement themeNode = node.Element(ns + "Theme");
            if(themeNode != null) {
                Theme = themeNode.Attribute("Name").Value;
            }

            if(node.Attribute("InstantFeedback") != null) {
                InstantFeedback = bool.Parse(node.Attribute("InstantFeedback").Value);
            }

            if(node.Element(ns + "CorrectAnswer") != null) {
                CorrectAnswerOverride = Uri.UnescapeDataString(node.Element(ns + "CorrectAnswer").Attribute("Text").Value);
            }
            if(node.Element(ns + "IncorrectAnswer") != null) {
                IncorrectAnswerOverride = Uri.UnescapeDataString(node.Element(ns + "IncorrectAnswer").Attribute("Text").Value);
            }

        }

        public override XElement ToXml() {
            XElement node = base.ToXml();
            node.Add(new XElement(ns + "Prompt", new XAttribute("Text", Prompt ?? string.Empty)));

            Dictionary<Category, int> categoryIDMapping = new Dictionary<Category, int>();
            XElement categoriesNode = new XElement(ns + "Categories");
            int id = 0;
            foreach(Category c in Categories) {
                categoryIDMapping[c] = id++;
                categoriesNode.Add(new XElement(ns + "Category", new XAttribute("Text", Uri.EscapeDataString(c.Text ?? string.Empty)), new XAttribute("ID", categoryIDMapping[c])));
            }
            node.Add(categoriesNode);

            XElement wordsNode = new XElement(ns + "Words");
            foreach(Word w in Words) {
                XElement wNode = new XElement(ns + "Word", new XAttribute("Text", Uri.EscapeDataString(w.Text ?? string.Empty)));
                if(w.CorrectCategory != null && categoryIDMapping.ContainsKey(w.CorrectCategory)) {
                    wNode.Add(new XAttribute("CorrectCategory", categoryIDMapping[w.CorrectCategory]));
                }
                wordsNode.Add(wNode);
            }
            node.Add(wordsNode);

            if(!(string.IsNullOrWhiteSpace(Theme))) {
                node.Add(new XElement(ns + "Theme", new XAttribute("Name", Theme)));
            }

            if(InstantFeedback) {
                node.Add(new XAttribute("InstantFeedback", InstantFeedback));
            }
            if(!string.IsNullOrWhiteSpace(CorrectAnswerOverride)) {
                node.Add(new XElement(ns + "CorrectAnswer", new XAttribute("Text", CorrectAnswerOverride)));
            }
            if(!string.IsNullOrWhiteSpace(IncorrectAnswerOverride)) {
                node.Add(new XElement(ns + "IncorrectAnswer", new XAttribute("Text", IncorrectAnswerOverride)));
            }

            return node;
        }

        //Removes blank words and categories
        public void PruneEmptyItems() {

            List<Word> wordsToDelete = new List<Word>();
            foreach(Word w in Words) {
                if(string.IsNullOrWhiteSpace(w.Text)) {
                    wordsToDelete.Add(w);
                }
            }
            foreach(Word w in wordsToDelete) {
                Words.Remove(w);
            }

            List<Category> categoriesToDelete = new List<Category>();
            foreach(Category c in Categories) {
                if(string.IsNullOrEmpty(c.Text)) {
                    categoriesToDelete.Add(c);
                }
            }
            
            foreach(Category c in categoriesToDelete) {
                //Remove correct CategoryReferences
                foreach(Word w in Words) {
                    if(w.CorrectCategory == c) {
                        w.CorrectCategory = null;
                    }
                }
                Categories.Remove(c);
            }
        }

        public CategoriesUserData InitUserData() {
            CategoriesUserData userData = new CategoriesUserData(1);
            for(int i = 0; i < this.Categories.Count; i++) {
                userData.AddCategory(i + 1);
            }

            return userData;
        }

        public override XElement GetReportContext(AggregateUserData userData) {
            XElement data = userData.ToXml();
            data.Add(new XAttribute("Question", Question));
            foreach(XElement c in data.Elements(ns + "Category")) {
                int catId = int.Parse(c.Attribute("ID").Value);
                c.Add(new XAttribute("Name", Categories[catId - 1].Text));
                foreach(XElement i in c.Elements(ns + "Item")) {
                    int itemId = int.Parse(i.Attribute("ID").Value);
                    i.Add(new XAttribute("Text", Words[itemId - 1].Text));
                }
            }
            return data;
        }


        public XElement GetGradingConfig() {
            XElement gradeNode = new XElement(ns + "Grading");

            if(HasAnswer && InstantFeedback) {
                gradeNode.Add(new XAttribute("InstantFeedback", InstantFeedback));
            }

            string correctAnswerFeedback = CorrectAnswerOverride ?? string.Empty;
            if(string.IsNullOrWhiteSpace(correctAnswerFeedback)) {
                correctAnswerFeedback = GetDefaultCorrectText();
            }
            gradeNode.Add(new XAttribute("CorrectAnswerFeedback", Uri.EscapeDataString(correctAnswerFeedback)));

            string incorrectAnswerFeedback = IncorrectAnswerOverride;
            if(string.IsNullOrWhiteSpace(incorrectAnswerFeedback)) {
                incorrectAnswerFeedback = GetDefaultIncorrectText();
            }
            gradeNode.Add(new XAttribute("IncorrectAnswerFeedback", Uri.EscapeDataString(incorrectAnswerFeedback)));

            gradeNode.Add(new XElement(ns + "Question", new XAttribute("Text", Question)));

            int id = 0;
            foreach(Category c in Categories) {
                id++;
                XElement newCategory = new XElement(ns + "Category", new XAttribute("ID", id), new XAttribute("Text", c.Text));
                gradeNode.Add(newCategory);
            }

            id = 0;
            foreach(Word w in Words) {
                id++;
                XElement newWord = new XElement(ns + "Word", new XAttribute("ID", id));
                if(w.CorrectCategory != null) {
                    for(int cID = 0; cID < Categories.Count; cID++) {
                        if(Categories[cID] == w.CorrectCategory) {
                            newWord.Add(new XAttribute("CorrectCategory", cID + 1));
                        }
                    }
                    gradeNode.Add(newWord);
                }
            }

            return gradeNode;
        }

        internal string GetDefaultCorrectText() {
            return "That's correct!";
        }

        internal string GetDefaultIncorrectText() {
            string incorrectText = "Sorry, the correct answers are\n";

            foreach(Category cat in Categories){
                incorrectText += "\t" + cat.Text + " (";
                foreach(Word word in Words) {
                    if(cat == word.CorrectCategory) {
                        incorrectText += word.Text + ", ";                        
                    }
                }

                incorrectText = incorrectText.TrimEnd(", ".ToCharArray());
                incorrectText += ")\n";
            }

            return incorrectText;
        }
    }

    

    public class Category
    {
        public Category(string text = null) {
            Text = text;
        }

        public string Text { get; set; }
    }

    public class Word
    {
        public Word(string text = null) {
            Text = text;
        }

        public string Text { get; set; }
        public Category CorrectCategory { get; set; }
    }
}
